package com.googlecode.yatspec.junit;

import com.googlecode.totallylazy.Callable1;
import com.googlecode.totallylazy.Pair;
import com.googlecode.yatspec.rendering.ScenarioNameRendererFactory;
import com.googlecode.yatspec.state.ScenarioName;
import org.junit.runners.model.FrameworkMethod;

import java.lang.reflect.Type;

import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.totallylazy.Sequences.zip;
import static java.util.Arrays.asList;

public class DecoratingFrameworkMethod extends FrameworkMethod {
    private final Row row;
    private final ArgumentCoercer argumentCoercer;

    public DecoratingFrameworkMethod(FrameworkMethod method, Row row) {
        this(method, row, new OnlyStringArguments());
    }

    public DecoratingFrameworkMethod(FrameworkMethod method, Row row, ArgumentCoercer argumentCoercer) {
        super(method.getMethod());
        this.row = row;
        this.argumentCoercer = argumentCoercer;
    }

    @Override
    public Object invokeExplosively(Object target, Object... params) throws Throwable {
        return super.invokeExplosively(target, arguments());
    }

    private Object[] arguments() {
        return zip(sequence(getMethod().getGenericParameterTypes()), sequence(row.value())).map(typedArgument()).toArray(Object.class);
    }

    private Callable1<? super Pair<Type, String>, Object> typedArgument() {
        return new Callable1<Pair<Type, String>, Object>() {
            public Object call(Pair<Type, String> pair) throws Exception {
                return argumentCoercer.get(pair.first(), pair.second());
            }
        };
    }

    @Override
    public String getName() {
        ScenarioName scenarioName = new ScenarioName(super.getName(), asList(row.value()));
        return ScenarioNameRendererFactory.renderer().render(scenarioName);
    }
}
